package com.zte.claa.inficombo.csa.app.service.persistent.impl;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.sql.DataSource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.alibaba.druid.pool.DruidDataSource;
import com.zte.claa.inficombo.csa.app.dao.mysql.impl.ClaaDevInitStateMapper;
import com.zte.claa.inficombo.csa.app.dao.mysql.impl.DevStateMapper;
import com.zte.claa.inficombo.csa.app.dao.mysql.impl.DevVersionMapper;
import com.zte.claa.inficombo.csa.app.dao.mysql.jdbcdao.AppDataDao;
import com.zte.claa.inficombo.csa.app.dao.mysql.jdbcdao.AppDataQueryPara;
import com.zte.claa.inficombo.csa.app.dao.mysql.jdbcdao.ClaaLocRawDataDao;
import com.zte.claa.inficombo.csa.app.dao.mysql.jdbcdao.ResIndexDataDao;
import com.zte.claa.inficombo.csa.app.model.dev.ClaaDevInitState;
import com.zte.claa.inficombo.csa.app.model.dev.DevBase;
import com.zte.claa.inficombo.csa.app.model.dev.DevJoinState;
import com.zte.claa.inficombo.csa.app.model.dev.DevRunState;
import com.zte.claa.inficombo.csa.app.model.dev.DevTpExtData;
import com.zte.claa.inficombo.csa.app.model.dev.DevTypeBase;
import com.zte.claa.inficombo.csa.app.model.dev.DevTypeModel;
import com.zte.claa.inficombo.csa.app.model.proj.ProjectDBPara;
import com.zte.claa.inficombo.csa.app.model.redis.DevMeteData;
import com.zte.claa.inficombo.csa.app.model.version.DevVersionUpdateLog;
import com.zte.claa.inficombo.csa.app.service.csa.impl.BaseCacheManager;
import com.zte.claa.inficombo.csa.app.service.persistent.CSADataPersistService;
import com.zte.claa.inficombo.csa.app.util.SpringBeans;

@Service
public class CSADataPersistServiceImpl implements CSADataPersistService {
	// driud连接池注册的springboot bean名称
	private static final String DRIUD_SB_BEAN_NAME = "druidDataSource";
		
	// 日志
	private Logger logger = LoggerFactory.getLogger(CSADataPersistServiceImpl.class);
	
	/**
	 * 各project的业务数据库映射表.
	 * key: projectid;
	 * val: DruidDataSource;
	 */
	private Map<String, DruidDataSource> projectDSMap = new HashMap<String, DruidDataSource>();
	
	// combodb基础库对应的datasource
	private DruidDataSource combodbDatasource = null;
	
	@Autowired
	private DevStateMapper devStateMapper;
	
	@Autowired
	private BaseCacheManager cacheManager;
	
	@Autowired
	private ClaaDevInitStateMapper devInitStateMapper;
	
	@Autowired
	private DevVersionMapper devVerMapper;
	
	// 保存claa自研产品的初始化状态及颁布信息
	public int saveClaaDevInitState(ClaaDevInitState state) throws Exception {
		if(state == null){
			return 0;
		}
		int retcode = this.devInitStateMapper.updateClaaDevInitState(state);
		if(retcode == 0){
			this.devInitStateMapper.addClaaDevInitState(state);
			retcode = 1;
		}
		return retcode;
	}

	/**
	 * 更新devjoin数据.
	 */
	@Override
	public int updateDevJoinState(DevJoinState state) throws Exception {
		int retcode = this.devStateMapper.updateDevJoinState(state);
		this.devStateMapper.updateSensorDevJoinStateByDtu(state);
		DevBase dev = this.cacheManager.getDev(state.getDevEUI());
		if(dev != null && dev.getRunState() != DevBase.RUN_STATE_JOIN){
			dev.setRunState(DevBase.RUN_STATE_JOIN);
			this.cacheManager.putDev(dev);		
		}
		return retcode;
	}

	/**
	 * 更新dev runstate数据
	 */
	@Override
	public int updateDevRunState(DevRunState state, boolean forceUpdateDB) throws Exception {
		boolean isUpdateDB = forceUpdateDB;
		DevBase dev = this.cacheManager.getDev(state.getDevEUI());
		if(dev != null && dev.getRunState() != state.getRunState()){
			dev.setRunState(state.getRunState());
			this.cacheManager.putDev(dev);	
			isUpdateDB = true;
		}
		if(isUpdateDB){
			int retcode = this.devStateMapper.updateDevRunState(state);
			this.devStateMapper.updateSensorDevRunStateByDtu(state);
			return retcode;
		}
		return 0;
	}
	
	/**
	 * 获取combo基础库的数据源
	 * 
	 * @return
	 */
	private DruidDataSource getComboDBDatasource(){
		if(this.combodbDatasource == null){
			this.combodbDatasource = (DruidDataSource)SpringBeans.getBean(DRIUD_SB_BEAN_NAME);
		}
		return this.combodbDatasource;
	}
	
	/**
	 * 保存metedata
	 * 
	 */
	public void saveDevMeteData(DevMeteData meteData, String tabName) throws Exception {
		String projId = meteData.getProjectid();
		DruidDataSource ds = this.getDataSource(projId);
		AppDataDao dao = new AppDataDao(ds);
		dao.saveDevMeteData(meteData, tabName);
	}
	
	/**
	 * 保存aicmetedata
	 * 
	 */
	public void saveAicDevMeteData(DevMeteData meteData, String tabName, String photoid) throws Exception {
		String projId = meteData.getProjectid();
		DruidDataSource ds = this.getDataSource(projId);
		AppDataDao dao = new AppDataDao(ds);
		dao.saveAicDevMeteData(meteData, tabName, photoid);
	}	
	
	/**
	 * 更新aicmetedata
	 * 
	 */
	public void updateAicDevMeteData(DevMeteData meteData, String tabName, String photoid) throws Exception {
		String projId = meteData.getProjectid();
		DruidDataSource ds = this.getDataSource(projId);
		AppDataDao dao = new AppDataDao(ds);
		dao.updateAicDevMeteData(meteData, tabName, photoid);
	}		
	
	// 批量保存metedata数据。
	public void saveDevMeteDataByBatch(List<DevMeteData> meteDatas, String tabName) throws Exception {
		if(meteDatas == null || meteDatas.size() == 0){
			logger.info("No data to be saved: "+tabName);
			return;
		}
		String projId = meteDatas.get(0).getProjectid();
		DruidDataSource ds = this.getDataSource(projId);
		AppDataDao dao = new AppDataDao(ds);
		dao.saveDevMeteDataByBatch(meteDatas, tabName);
	}
	
	// 查询deveui查询最近上报的数据metedata
	public Map<String, Object> queryDevLatestMeteDataAsMap(String devEui) throws Exception {
		if(devEui == null || devEui.trim().equals("")){
			return null;
		}
		DevBase dev = this.cacheManager.getDev(devEui);
		if(dev == null){
			logger.error("Dev not exist in cache: "+devEui);
			return null;
		}
		DevTypeBase devTp = this.cacheManager.getDevTypeBase(dev.getDevType());
		if(devTp == null){
			logger.error("Devtype not exist in cache: "+dev.getDevType());
			return null;
		}
		String tabName = devTp.getTbname();
		String projectId = dev.getProjectId();
		DruidDataSource ds = this.getDataSource(projectId);
		List<String> devEuiList = new ArrayList<String>();
		devEuiList.add(devEui);
		AppDataQueryPara para = new AppDataQueryPara();
		para.setBatch(10);
		para.setDevEUIs(devEuiList);
		para.setDevType(dev.getDevType());
		para.setPageIndex(1);
		para.setProjectId(projectId);
		AppDataDao dao = new AppDataDao(ds);
		DevTpExtData dtpExtData = this.cacheManager.getDevTpExtData(dev.getDevType());
		// 支持逻辑资源的index值查询...
		if(dtpExtData != null && dtpExtData.getFntype() == DevTpExtData.FN_TP_8192){
			dao = new ResIndexDataDao(ds);
		}		
		List<Map<String, Object>> mapList = dao.queryDevLatestMeteDataAsMap(para, tabName);
		if(mapList == null || mapList.size() == 0){
			logger.warn("LatestMeteData(3days) not exist for: "+devEui);
			return null;
		}
		return mapList.get(0);
	}
	
	// 保存定位原始数据.
	public void saveLocRawData(DevMeteData meteData, String tabName) throws Exception {
		ClaaLocRawDataDao dao = new ClaaLocRawDataDao(this.getComboDBDatasource());
		dao.saveLocRawData(meteData, tabName);
	}
	
	// 批量保存定位原始数据.
	public void saveLocRawDataByBatch(List<DevMeteData> meteDatas, String tabName) throws Exception {
		ClaaLocRawDataDao dao = new ClaaLocRawDataDao(this.getComboDBDatasource());
		dao.saveLocRawDataByBatch(meteDatas, tabName);
	}
	
	// 更新终端版本升级上报信息：DevVersionUpdateLog. csa侧不需要设置taskid，更新脚本自动筛选当前最新升级任务进行匹配更新.
	public int modifyDevVersionUpdateLog(DevVersionUpdateLog log) throws Exception {
		return this.devVerMapper.modifyDevVersionUpdateLog(log);
	}
	
	/**
	 * 获取业务模型
	 * 
	 * @param devTpId
	 * @return
	 * @throws Exception
	 */
	public DevTypeModel getDevTpModel(String devTpId) throws Exception {
		DevTypeModel dtm = this.cacheManager.getDevTypeModel(devTpId);
		if(dtm == null || dtm.getDmList() == null){
			logger.error("[*!!*]Can not get datamodel for devtype="+devTpId);
		}
		return dtm;
	}
	
	/**
	 * 获取project对应的业务数据库ds
	 * 
	 * @param projectId
	 * @return
	 */
	public DruidDataSource getDataSource(String projectId) throws Exception {
		DruidDataSource ds = this.projectDSMap.get(projectId);
		if( ds != null){
			return ds;
		}
		synchronized(this.projectDSMap){
			ds = this.projectDSMap.get(projectId);
			if( ds != null){
				return ds;
			}
			ProjectDBPara dbPara = this.cacheManager.getProjDBPara(projectId);
			if(dbPara == null || ProjectDBPara.INVALID_IP.equalsIgnoreCase(dbPara.getDbip())){
				// 如果不配置具体项目的dbpara信息，表示其数据库和combodb基础库在同一个数据库服务器上.
				// throw new Exception("Can get DB para for project: "+projectId);
				logger.info("Project dbpara is null, use combodb as default: "+projectId);
				return (DruidDataSource)SpringBeans.getBean(DRIUD_SB_BEAN_NAME);
			}
			// url: jdbc:mysql://localhost:3306/combodb?characterEncoding=utf8&allowMultiQueries=true
			StringBuffer sb = new StringBuffer();
			sb.append("jdbc:mysql://").append(dbPara.getDbip()).append(":").append(dbPara.getDbport());
			sb.append("/").append(dbPara.getDbname()).append("?characterEncoding=utf8&allowMultiQueries=true&serverTimezone=GMT%2B8&useSSL=false");			
			return this.createDataSource(projectId, sb.toString(), dbPara.getDbuser(), dbPara.getDbpswd());
		}
	}
	
	/**
     * 创建数据源
     * 
     * url: jdbc:mysql://localhost:3306/combodb?characterEncoding=utf8&amp;allowMultiQueries=true
     */
    private DruidDataSource createDataSource(String projectId, String url, String username,String password) {
        DruidDataSource parent = (DruidDataSource)SpringBeans.getBean(DRIUD_SB_BEAN_NAME);
        DruidDataSource ds = new DruidDataSource();
        ds.setUrl(url);
        ds.setName(projectId);
        ds.setUsername(username);
        ds.setPassword(password);
        ds.setDriverClassName(parent.getDriverClassName());
        ds.setInitialSize(parent.getInitialSize());
        ds.setMinIdle(parent.getMinIdle());
        ds.setMaxActive(parent.getMaxActive());
        ds.setMaxWait(parent.getMaxWait());
        ds.setTimeBetweenConnectErrorMillis(parent.getTimeBetweenConnectErrorMillis());
        ds.setTimeBetweenEvictionRunsMillis(parent.getTimeBetweenEvictionRunsMillis());
        ds.setMinEvictableIdleTimeMillis(parent.getMinEvictableIdleTimeMillis());
        ds.setValidationQuery(parent.getValidationQuery());
        ds.setTestWhileIdle(parent.isTestWhileIdle());
        ds.setTestOnBorrow(parent.isTestOnBorrow());
        ds.setTestOnReturn(parent.isTestOnReturn());
        ds.setRemoveAbandoned(parent.isRemoveAbandoned());
        ds.setRemoveAbandonedTimeoutMillis(parent.getRemoveAbandonedTimeoutMillis());
        ds.setLogAbandoned(parent.isLogAbandoned());
        // 只要maxPoolPreparedStatementPerConnectionSize>0,poolPreparedStatements就会被自动设定为true，参照druid的源码
        ds.setMaxPoolPreparedStatementPerConnectionSize(parent.getMaxPoolPreparedStatementPerConnectionSize());
        this.projectDSMap.put(projectId, ds);
        return ds;
    }
	
    //使项目数据源失效
  	public void invalidDataSource(String projectId){
  		//先移除，再决定是否需要关闭数据源
  		DataSource dataSource = this.projectDSMap.remove(projectId);
  		if (dataSource == null)	{
  			return;
  		}
  		//只关闭该项目的数据源
  		if (dataSource instanceof DruidDataSource){
  			DruidDataSource druidDataSource = (DruidDataSource)dataSource;
  			if (druidDataSource.getName().equals(projectId)){
  				druidDataSource.close();
  			}
  		}
  	}
  	
  	// metedata缓存服务.
  	public void putMeteDataToCache(DevMeteData data){
  		this.cacheManager.putCombineData(data);
  	}
  	
  	public void removeMeteDataFromCache(String devEui) {
  		this.cacheManager.removeCombineData(devEui);
  	}
  	
  	public DevMeteData getMeteDataFromCache(String devEui) {
  		return this.cacheManager.getCombineData(devEui);
  	}
}
